import {SearchBookResult, Book, BasicChapter, BookSource} from './model/book.js'

class AbstractBookCrawler {

    /**
     *
     * @param {BookSource} bookSource
     */
    constructor(bookSource) {
        this.source = bookSource;
    }

    /**
     *
     * @param {String} keyword
     * @param {Number} pageNum
     * @returns {Promise<SearchBookResult>}
     */
    async searchBook(keyword, pageNum) {
        throw new Error('Method "searchBook()" must be implemented in a subclass.');
    }

    /**
     *
     * @param {String} path
     * @returns {Promise<Book>}
     */
    async bookDetails(path) {
        throw new Error('Method "bookDetails()" must be implemented in a subclass.');
    }

    /**
     *
     * @param {String} path path = catalogPath ?? path，如果搜索结构item中有catalogPath，则优先使用catalogPath,否则使用path
     * @returns {Promise<Array<BasicChapter>>}
     */
    async bookCatalog(path) {
        throw new Error('Method "bookCatalog()" must be implemented in a subclass.');
    }

    /**
     *
     * @param {String} path
     * @returns {Promise<String>}
     */
    async chapterContent(path) {
        throw new Error('Method "chapterContent()" must be implemented in a subclass.');
    }
}

class BookCrawlerTest {
    /**
     * 测试书源爬虫
     * @param {AbstractBookCrawler} bookCrawler
     * @param {Boolean} enableSearch，默认为true
     * @param {String|null} keyword 搜索关键词
     * @param {Number} pageNum 搜索页数
     * @param {String|null} bookPath 需要测试的书籍内容的path，与 bookIndex 二选一
     * @param {Number|null} bookIndex 需要测试的书籍内容的index(为搜索结果的index，如果enableSearch为false，则无效)
     * @param {Number|Array<Number>} chapterIndex 需要测试的章节的index
     */
    constructor(bookCrawler, {
        enableSearch = true,
        keyword = null,
        pageNum = 1,
        bookPath = null,
        catalogPath = null,
        bookIndex = 0,
        chapterIndex = 0
    } = {}) {
        if (!bookCrawler) throw new Error('BookCrawlerTest: 参数 "bookCrawler" 是必填项。');
        if (typeof enableSearch !== 'boolean') throw new Error('BookCrawlerTest: 参数 "enableSearch" 必须是布尔值。');
        if (enableSearch && !keyword) {
            throw new Error('BookCrawlerTest: 开启搜索则参数 "keyword" 必填。');
        }
        if (keyword && typeof keyword !== 'string') throw new Error('BookCrawlerTest: 参数 "keyword" 必须是字符串。');
        if (typeof pageNum !== 'number' || pageNum < 1) throw new Error('BookCrawlerTest: 参数 "pageNum" 必须是大于0的数字。');
        if (bookPath && typeof bookPath !== 'string') throw new Error('BookCrawlerTest: 参数 "bookPath" 必须是字符串。');
        if (catalogPath && typeof catalogPath !== 'string') throw new Error('BookCrawlerTest: 参数 "catalogPath" 必须是字符串。');

        // 需要判断是否开启了搜索
        bookIndex = enableSearch ? bookIndex : null;
        if (bookIndex && typeof bookIndex !== 'number') throw new Error('BookCrawlerTest: 参数 "bookIndex" 必须是数字。');
        if (typeof chapterIndex !== 'number' && !Array.isArray(chapterIndex)) throw new Error('BookCrawlerTest: 参数 "chapterIndex" 必须是数字或数字数组。');
        if (!bookPath && typeof bookIndex !== 'number') throw new Error('BookCrawlerTest: 参数 "bookPath" 和 "bookIndex" 不能同时为null。');
        this.bookCrawler = bookCrawler;
        this.enableSearch = enableSearch;
        this.keyword = keyword;
        this.pageNum = pageNum;
        this.bookPath = bookPath;
        this.catalogPath = catalogPath;
        this.bookIndex = bookIndex;
        this.chapterIndex = chapterIndex;
    }

    async run() {
        if (this.enableSearch) {
            const searchBookResult = await this.bookCrawler.searchBook(this.keyword, this.pageNum);
            console.log("搜索结果: ", searchBookResult);
            if (!this.bookPath) {
                if (searchBookResult.size > 0) {
                    const book = searchBookResult.bookList[Math.min(this.bookIndex, searchBookResult.size - 1)];
                    this.bookPath = book.path;
                    this.catalogPath = book.catalogPath;
                } else {
                    throw new Error("无搜索内容，请填写参数: 'bookPath' 或更换关键词搜索");
                }
            }
        }
        const book = await this.bookCrawler.bookDetails(this.bookPath);
        console.log("书籍详情: ", book);
        const catalog = await this.bookCrawler.bookCatalog(this.catalogPath ?? this.bookPath);
        console.log("书籍目录");
        if (catalog.length > 10) {
            for (let i = 0; i < 5; i++) {
                console.log(catalog[i]);
            }
            console.log("......");
            for (let i = 5; i > 0; i--) {
                console.log(catalog[catalog.length - i]);
            }
        } else {
            for (const item of catalog) {
                console.log(item);
            }
        }
        let chapterIndexArr;
        if (typeof this.chapterIndex === 'number') {
            chapterIndexArr = [this.chapterIndex];
        } else if (Array.isArray(this.chapterIndex)) {
            chapterIndexArr = this.chapterIndex;
        } else {
            throw new Error('BookCrawlerTest: 参数 "chapterIndex" 必须是数字或数字数组。');
        }

        console.log("章节内容");
        for (const chapterIndex of chapterIndexArr) {
            console.log("\n\n");
            const chapterContent = await this.bookCrawler.chapterContent(catalog[chapterIndex].path);
            console.log(catalog[chapterIndex].title);
            console.log("--" + chapterContent + "--");
        }

        // 退出控制台
        // process.exit();
    }
}


export {
    AbstractBookCrawler, BookCrawlerTest
}